package engine;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Scanner;

import engine.Project.ProjectProperty;

public class ARFFReader {

	public static ArrayList<Project> parse(String fileName) throws FileNotFoundException, IOException {
		File file = new File(fileName);
		
		ArrayList<ProjectPropertyDescriptor> arrDescriptors = getProjectDescriptors(fileName);
		HashMap<String, ProjectPropertyDescriptor> descriptors = new HashMap<String, ProjectPropertyDescriptor>();
		
		for (ProjectPropertyDescriptor ppd : arrDescriptors) {
			descriptors.put(ppd.getName(), ppd);
		}
		
		if (!file.exists())
			throw new FileNotFoundException();
		if (file.isDirectory())
			throw new IOException("The \"file\" is a directory");
		if (!file.canRead())
			throw new IOException("Couldn't read file");
		
		
		Scanner sc = new Scanner(file);
		
		List<String> data = new ArrayList<String>();
		
		while (sc.hasNextLine()) {
			String line = sc.nextLine().trim();
			
			if (line.isEmpty())
				continue;
			
			int parindex = line.indexOf('%');
			
			if (parindex == -1)
				parindex = line.length();
			
			line = line.substring(0, parindex).trim();
			
			if (line.isEmpty() || line.startsWith("%"))
					continue;
			
			data.add(line);
		}
		
		HashMap<Integer, ProjectProperty> possibleProperties = new HashMap<Integer, ProjectProperty>();
		ArrayList<Project> projects = new ArrayList<Project>();
		
		boolean isProjects = false;
		for (String s : data) {
			if (isProjects) {
				
				Project p = new Project();
				
				String[] vals = s.split(",");
				
				for (int i=0; i<vals.length; i++) {
					ProjectProperty pp = (ProjectProperty)possibleProperties.get(i).clone();
//					pp.set(vals[i]); //TODO lägg in igen, just nu craschar det
					pp.set(vals[i]); //kraschar fortfarande, lol
					p.addCharacteristic(pp);
				}
				projects.add(p);
				
			} else if (s.startsWith("@attribute") || s.startsWith("@ATTRIBUTE")) {
				
				//s = s.replace("\t", " ").replace("  ", " ");
				
				String[] props = splitAndParse(s);//s.split(" ");
				
				if (props.length != 3) {
					System.err.println("Invalid length: " + Arrays.toString(props));
					throw new IllegalArgumentException("File is invalid format. \"" + s +"\" couldn't be parsed as a project attribute");
				}
				
				int index = possibleProperties.size();
				
				
				
//				ProjectPropertyDescriptor ppd = descriptors.get(props[1]);
//				if (ppd == null) {
//					Logger.log(String.valueOf(props[1] +" : " + descriptors));
//				}
				possibleProperties.put(index, new ProjectProperty(props[1]));
				
			} else if (s.startsWith("@data") || s.startsWith("@DATA")) {
				
				isProjects = true;
				
			}
		}
		
		
		return projects;
	}
	
	
	
	private static String[] splitAndParse(String s) {
		
		ArrayList<String> msgs = new ArrayList<String>();
		boolean isRabbit = false;
		
		s = s.replace("\t", " ").replace("  ", " ");
		
		String[] props = s.split(" ");
		
		for (int i = 0; i < props.length; i++) {
			
			if (isRabbit) {
				String pre = msgs.remove(msgs.size()-1);
				msgs.add(pre +" " + props[i]);
			} else
				msgs.add(props[i]);
			
			if (props[i].startsWith("\""))
				isRabbit = true;
			else if (props[i].endsWith("\""))
				isRabbit = false;
		}
		
		return msgs.toArray(new String[]{});
	}
	
	public static ArrayList<ProjectPropertyDescriptor> getProjectDescriptors(String fileName) throws IOException {
		
		File file = new File(fileName);
		
		if (!file.exists())
			throw new FileNotFoundException();
		if (file.isDirectory())
			throw new IOException("The \"file\" is a directory");
		if (!file.canRead())
			throw new IOException("Couldn't read file");
		
		
		Scanner sc = new Scanner(file);
		
		
		
		
		List<String> data = new ArrayList<String>();
		
		while (sc.hasNextLine()) {
			String line = sc.nextLine().trim();
			
			if (line.isEmpty())
				continue;
			
			int parindex = line.indexOf('%');
			
			if (parindex == -1)
				parindex = line.length();
			
			line = line.substring(0, parindex).trim();
			
			if (line.isEmpty() || line.startsWith("%"))
					continue;
			
			data.add(line);
		}
		
		ArrayList<ProjectPropertyDescriptor> ret = new ArrayList<ProjectPropertyDescriptor>();
		
		for (String s : data) {
			if (s.startsWith("@attribute") || s.startsWith("@ATTRIBUTE")) {
				
				
				String[] props = splitAndParse(s);//s.split(" ");
				
				if (props.length != 3) {
					System.err.println("Invalid length: " + Arrays.toString(props));
					throw new IllegalArgumentException("File is invalid format. \"" + s +"\" couldn't be parsed as a project attribute");
				}
				
				String name = props[1];
				String opts = props[2];
				
				if (!opts.contains("{") || !opts.contains("{")) {
					//ret.add(new ProjectPropertyDescriptor(name, true));
				} else {
					
					opts = opts.substring(1, opts.length()-1);
					
					ArrayList<String> arrOpts = new ArrayList<String>();
					
					for (String st : opts.split(","))
						arrOpts.add(st);
					
					Collections.sort(arrOpts, new Comparator<String>(){

						@Override
						public int compare(String a, String b) {
							int ia = 0;
							if (Project.indexValues.containsKey(a))
								ia = Project.indexValues.get(a);
							
							int ib = 0;
							if (Project.indexValues.containsKey(b))
								ib = Project.indexValues.get(b);
							
							return ia-ib;
						}
						
					});
					
					ret.add(new ProjectPropertyDescriptor(name, arrOpts));
					
				}
			}
		}
		
		return ret;
	}
	
	public static class ProjectPropertyDescriptor {
		String name;
		ArrayList<String> options;
		boolean isNumber;
		
		public ProjectPropertyDescriptor(String name, ArrayList<String> options) {
			this.name = name;
			this.options = options;
			this.isNumber = false;
		}
		public ProjectPropertyDescriptor(String name, boolean isNumber) {
			this.name = name;
			this.isNumber = isNumber;
		}
		public String getName() {
			return name;
		}
		public String toString() {
			if (!isNumber)
				return String.valueOf(name) + " : " + String.valueOf(options);
			else
				return String.valueOf(name) + " <Number>";
		}
		public ArrayList<String>  getValues() {
			return options;
		}
		public boolean isNumber() {
			return isNumber();
		}
		@Override
		public boolean equals(Object obj) {
			if (obj instanceof ProjectPropertyDescriptor)
				return name.equals(((ProjectPropertyDescriptor)obj).name);
			else
				return false;
		}
		
		public double getRange() {
			return Project.indexValues.get(options.get(options.size()-1)) - Project.indexValues.get(options.get(0));
		}
		
	}
}

